package com.rainyun.rc.config;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.rainyun.rc.common.VersionException;
import com.rainyun.rc.config.annotation.DefDouble;
import com.rainyun.rc.config.annotation.DefInt;
import com.rainyun.rc.config.annotation.DefString;
import org.jetbrains.annotations.Nullable;

/**配置管理中心*/
public interface NodeConfig<T>{

	/**获取文件输出根路径*/
    String getRootPath();
	/**获取文件在jar内的读取根路径*/
	String getFromPath();
	/**设置文件输出根路径*/
	void setRootPath(String root);
	/**设置文件在jar内的读取根路径*/
	void setFromPath(String from);

	/**设置/添加配置
	 * @param name 配置名称
	 * @param path 配置路径(无需文件后缀),若以/结尾,则认为是该路径下的配置列表
	 * */
	NodeConfig<T> add(String name,String path);

	/**设置/添加配置,同时绑定枚举
	 * @param name 配置名称
	 * @param path 配置路径(无需文件后缀),若以/结尾,则认为是该路径下的配置列表
	 * @param e 要绑定的枚举(绑定枚举后可以快捷使用 get(Enum)方法来获取值)
	 * @see NodeConfig#get(Enum)
	 * */
	NodeConfig<T> add(String name,String path,Class<? extends Enum<?>> e);

	/**为配置绑定枚举*/
	NodeConfig<T> bindEnum(String name,Class<? extends Enum<?>> e);

	void setPaths(Map<String,String> paths);
	Map<String,String> getPaths();

	void load(String name) throws VersionException, Exception;

	void loadOrNew(String name) throws VersionException,Exception;

	void loadAll() throws VersionException,Exception;

	void loadAllOrNew() throws VersionException,Exception;

	void save(String name) throws IOException;

	void saveInFolder(String name,String file_name) throws IOException;

	void saveAll() throws IOException;

	/**获取配置*/
	T get(String name);

	/**通过绑定的枚举来获取配置值,如果枚举名为CONF_V1则对应配置conf.v1*/
	<V> V get(Enum<?> e);
	/**通过绑定的枚举来设置配置值*/
	void set(Enum<?> e,Object val);
	/**获取配置列表*/
	Map<String,T> getFolder(String name);

	/**(不常用)把配置对象存入缓存*/
	void set(String name,Object conf);

	/**设置异常处理器*/
	void setErrorHandler(ErrorHandler handler);

	/**设置配置项为纯资源文件模式<br>
	 * 该模式载入的资源为file对象,而不是配置对象<br>
	 * 所以该模式值不能获取配置值,也不支持save<br>
	 * 但可以通过getResource来获取file对象*/
	void setResource(String name,boolean b);
	/**是否配置项为纯资源文件*/
	boolean isResource(String name);
	/**获取纯资源文件(需要先setResource且完成load)*/
	File getResource(String name);

	/**开启后,该配置的默认文件会从所绑定的Enum载入(仅支持单文件配置)*/
	void setFromEnum(String name,boolean b);
	/**配置是否开启从Enum载入默认值*/
	boolean isFromEnum(String name);

	/**获取注释配置树,<br>
	 * 若该配置是一个配置列表,返回的配置树第一层键将是配置列表的各文件名
	 * */
	Map<String,Object> getDescTree(String name);
	/**设置注释配置树*/
	void setDescTree(String name,Map<String,Object> tree);

	/**开关配置的注释支持(默认开)
	 * 开启后,读取文件可以获得注释配置树<br>
	 * 使得保存文件可以保留注释<br>*/
	void setDesc(String name,boolean b);
	/**配置是否开启注释支持*/
	boolean isDesc(String name);

	/**开关文件版本检查(config_version节点检查)*/
	void setVerCheck(String name,boolean b);
	/**配置是否开启版本检查(config_version节点检查)*/
	boolean isVerCheck(String name);

	/**通过枚举类和及字段的默认值生成配置树(包括版本信息)*/
	T getTreeByEnum(Class<? extends Enum<?>> ec);


	/**通过枚举字段配置获得枚举配置值<br>
	 *即 通过 @DefString @DefInt 注解<br>
	 *或枚举内的 static getDefault(Enum en)方法*/
	public static Object getDefaultByEnum(Enum<?> en) {
		Class<?> ec =  en.getDeclaringClass();
		Method vm = null;
		try {
			vm = ec.getDeclaredMethod("getDefault",ec);
			vm.setAccessible(true);
		} catch (NoSuchMethodException | SecurityException e) {}

		Field f = null;
		try {
			f = ec.getDeclaredField(en.name());
		} catch (NoSuchFieldException | SecurityException e1) {
			e1.printStackTrace();
			return null;
		}

		f.setAccessible(true);
		Object def = null;
		if(f.isAnnotationPresent(DefString.class))
			def = f.getAnnotation(DefString.class).value();
		else if(f.isAnnotationPresent(DefInt.class))
			def = f.getAnnotation(DefInt.class).value();
		else if(f.isAnnotationPresent(DefDouble.class))
			def = f.getAnnotation(DefDouble.class).value();
		else if(vm!=null) {
			try {
				vm.invoke(null,f.get(null));
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return def;
	}

	/**从有注释格式的配置Map生成注释配置树
	 * @param confmap 配置数
	 * @return 注释配置树 */
	public static Map<String,Object> getDescTree(@Nullable Map<String,Object> confmap){
		Map<String,Object> tm = new LinkedHashMap<String, Object>();
		if(confmap!=null)NodeConfig.getDescTree(confmap, tm);
		return tm;
	}

	/**从有注释格式的配置Map生成注释配置树
	 * @param confmap 配置数
	 * @param tm 注释配置树(用引用传递获得注释配置树)*/
	public static void getDescTree(Map<String,Object> confmap,Map<String,Object> tm){
		for(Entry<String, Object> en:confmap.entrySet()) {
			if(en.getValue() instanceof Map) {
				Map<String,Object> newm = new LinkedHashMap<String, Object>();
				getDescTree((Map<String, Object>) en.getValue(),newm);
				if(!newm.isEmpty())
					tm.put(en.getKey(),newm);
			}else if(en.getKey().endsWith("_desc")){
				tm.put(en.getKey(),en.getValue());
			}
		}
	}
	
	
	
}
